(function(window, document, $) {
  function Game2048(opt) {
    var prefix = opt.prefix, len = opt.len, size = opt.size, margin = opt.margin;
    var score = 0;
    var winNum = 2048;
    var isGameOver = true;
    var board = new Board(len);
    var view = new View(prefix, len, size, margin);
    view.init();
    board.onGenerate = function(e) {
      view.addNum(e.x, e.y, e.num);
    };
    board.onMove = function(e) {
      if (e.to.num >= winNum) {
        isGameOver = true;
        setTimeout(function() { view.win(); }, 300);
      }
      if (e.to.num > e.from.num) {
        score += e.to.num;
        view.updateScore(score);
      }
      view.move(e.from, e.to);
    };
    board.onMoveComplete = function(e) {
      if (!board.canMove()) {
        isGameOver = true;
        setTimeout(function() { view.over(score); }, 300);
      }
      if (e.moved) {
        setTimeout(function(){ board.generate(); }, 200);
      }
    };
    $(document).keydown(function(e) {
      if (isGameOver) {
        return false;
      }
      switch (e.which) {
        case 37: board.moveLeft();  break;
        case 38: board.moveUp();    break;
        case 39: board.moveRight(); break;
        case 40: board.moveDown();  break;
      }
    });
    function start() {
      score = 0;
      view.updateScore(0);
      view.cleanNum();
      board.init();
      board.generate();
      board.generate();
      isGameOver = false;
    }
    $('#' + prefix + '_restart').click(start);
    start();
  };
  // 数据处理
  function Board(len) {
    this.len = len;
    this.arr = [];
  }
  Board.prototype = {
    // 事件
    onGenerate: function() {},
    onMove: function() {},
    onMoveComplete: function() {},
    // 创建数组
    init: function() {
      for (var arr = [], x = 0, len = this.len; x < len; ++x) {
        arr[x] = [];
        for (var y = 0; y < len; ++y) {
            arr[x][y] = 0;
          }
      }
      this.arr = arr;
    },
    // 在随机位置增加一个随机数
    generate: function() {
      var empty = [];
      for (var x = 0, arr = this.arr, len = arr.length; x < len; ++x) {
        for (var y = 0; y < len; ++y) {
          if (arr[x][y] === 0) {
            empty.push({x: x, y: y});
          }
        }
      }
      if (empty.length < 1) {
        return false;
      }
      var pos = empty[Math.floor((Math.random() * empty.length))];
      this.arr[pos.x][pos.y] = Math.random() < 0.5 ? 2 : 4;
      this.onGenerate({x: pos.x, y: pos.y, num: this.arr[pos.x][pos.y]});
    },
    // 左移
    moveLeft: function() {
      var canMove = false;
      // 从上到下，从左到右
      for (var x = 0, len = this.arr.length; x < len; ++x) {
        for (var y = 0, arr = this.arr[x]; y < len; ++y) {
          // 从 y + 1 位置开始，向右查找
          for (var next = y + 1; next < len; ++next) {
            // 如果 next 单元格是 0，找下一个不是 0 的单元格
            if (arr[next] === 0) {
              continue;
            }
            // 如果 y 数字是 0，则将 next 移动到 y 位置，然后将 y 减 1 重新查找
            if (arr[y] === 0) {
              arr[y] = arr[next];
              this.onMove({from: {x: x, y: next, num: arr[next]}, to: {x: x, y: y, num: arr[y]}});
              arr[next] = 0;
              canMove = true;
              --y;
            // 如果 y 与 next 单元格数字相等，则将 next 移动并合并给 y
            } else if (arr[y] === arr[next]) {
              arr[y] += arr[next];
              this.onMove({from: {x: x, y: next, num: arr[next]}, to: {x: x, y: y, num: arr[y]}});
              arr[next] = 0;
              canMove = true;
            }
            break;
          }
        }
      }
      this.onMoveComplete({moved: canMove});
    },
    moveRight: function() {
      var moved = false;
       for (var x = 0, len = this.arr.length; x < len; ++x) {
        for (var y = len - 1, arr = this.arr[x]; y >= 0; --y) {
          for (var prev = y - 1; prev >= 0; --prev) {
            if (arr[prev] === 0) {
              continue;
            }
            if (arr[y] === 0) {
              arr[y] = arr[prev];
              this.onMove({from: {x: x, y: prev, num: arr[prev]}, to: {x: x, y: y, num: arr[y]}});
              arr[prev] = 0;
              moved = true;
              ++y;
            } else if (arr[y] === arr[prev]) {
              arr[y] += arr[prev];
              this.onMove({from: {x: x, y: prev, num: arr[prev]}, to: {x: x, y: y, num: arr[y]}});
              arr[prev] = 0;
              moved = true;
            }
            break;
          }
        }
      }
      this.onMoveComplete({moved: moved});
    },
    moveUp: function() {
      var canMove = false;
      for (var arr = this.arr, len = arr.length, y = 0; y < len; ++y) {
        for (var x = 0; x < len; ++x) {
          for (var next = x + 1; next < len; ++next) {
            if (arr[next][y] === 0) {
              continue;
            }
            if (arr[x][y] === 0) {
              arr[x][y] = arr[next][y];
              this.onMove({from: {x: next, y: y, num: arr[next][y]}, to: {x: x, y: y, num: arr[x][y]}});
              arr[next][y] = 0;
              canMove = true;
              --x;
            } else if (arr[x][y] === arr[next][y]) {
              arr[x][y] += arr[next][y];
              this.onMove({from: {x: next, y: y, num: arr[next][y]}, to: {x: x, y: y, num: arr[x][y]}});
              arr[next][y] = 0;
              canMove = true;
            }
            break;
          }
        }
      }
      this.onMoveComplete({moved: canMove});
    },
    moveDown: function() {
      var canMove = false;
      for (var arr = this.arr, len = arr.length, y = 0; y < len; ++y) {
        for (var x = len - 1; x >= 0; --x) {
          for (var prev = x - 1; prev >= 0; --prev) {
            if (arr[prev][y] === 0) {
              continue;
            }
            if (arr[x][y] === 0) {
              arr[x][y] = arr[prev][y];
              this.onMove({from: {x: prev, y: y, num: arr[prev][y]}, to: {x: x, y: y, num: arr[x][y]}});
              arr[prev][y] = 0;
              canMove = true;
              ++x;
            } else if (arr[x][y] === arr[prev][y]) {
              arr[x][y] += arr[prev][y];
              this.onMove({from: {x: prev, y: y, num: arr[prev][y]}, to: {x: x, y: y, num: arr[x][y]}});
              arr[prev][y] = 0;
              canMove = true;
            }
            break;
          }
        }
      }
      this.onMoveComplete({moved: canMove});
    },
    canMove: function() {
      for (var x = 0, arr = this.arr, len = arr.length; x < len; ++x) {
        for (var y = 0; y < len; ++y) {
          if (arr[x][y] === 0) {
            return true;
          }
          var curr = arr[x][y], right = arr[x][y + 1];
          var down = arr[x + 1] ? arr[x + 1][y] : null;
          if (right === curr || down === curr) {
            return true;
          }
        }
      }
      return false;
    }
  };
  // 视图处理
  function View(prefix, len, size, margin) {
    this.prefix = prefix;
    this.len = len;             // 单元格单边的数量（实际数量 len * len）
    this.size = size;	        // 每个单元格的边长
    this.margin = margin;	// 每个单元格的间距
    this.score = $('#' + prefix + '_score');
    this.container = $('#' + prefix + '_container');
    var containerSize = len * size + margin * (len + 1);
    this.container.css({width:containerSize , height: containerSize});
    this.nums = {};
  }
  View.prototype = {
    // 计算位置
    getPos: function(n) {
      return this.margin + n * (this.size + this.margin);
    },
    init: function() {
      for (var x = 0, len = this.len; x < len; ++x) {
        for (var y = 0; y < len; ++y) {
          var $cell = $('<div class="' + this.prefix + '-cell"></div>');
          $cell.css({
              width: this.size + 'px', height: this.size + 'px', 
              top: this.getPos(x), left: this.getPos(y)
          }).appendTo(this.container);
        }
      }
    },
    addNum: function(x, y, num) {
      var $num = $('<div class="' + this.prefix + '-num ' + this.prefix + '-num-' + num + ' ">');
      $num.text(num).css({
        top: this.getPos(x) + parseInt(this.size / 2),
        left: this.getPos(y) + parseInt(this.size / 2)
      }).appendTo(this.container).animate({
        width: this.size + 'px',
        height: this.size + 'px',
        lineHeight: this.size + 'px',
        top: this.getPos(x),
        left: this.getPos(y)
      }, 100);
      this.nums[x + '-' + y] = $num;
    },
    move: function(from, to) {
      var fromIndex = from.x + '-' + from.y, toIndex = to.x + '-' + to.y;
      var clean = this.nums[toIndex];
      this.nums[toIndex] = this.nums[fromIndex];
      delete this.nums[fromIndex];
      var prefix = this.prefix + '-num-';
      var pos = {top: this.getPos(to.x), left: this.getPos(to.y)};
      this.nums[toIndex].finish().animate(pos, 200, function() {
        if (to.num > from.num) {
          clean.remove();
          $(this).text(to.num).removeClass(prefix + from.num).addClass(prefix + to.num);
        }
      });
    },
    updateScore: function(score) {
      this.score.text(score);
    },
    win: function() {
      $('#' + this.prefix + '_over_info').html('<p>您获胜了</p>');
      $('#' + this.prefix + '_over').removeClass(this.prefix + '-hide');
    },
    over: function(score) {
      $('#' + this.prefix + '_over_info').html('<p>本次得分</p><p>' + score + '</p>');
      $('#' + this.prefix + '_over').removeClass(this.prefix + '-hide');
    },
    cleanNum: function() {
      this.nums = {};
      $('#' + this.prefix + '_over').addClass(this.prefix + '-hide');
      $('.' + this.prefix + '-num').remove();
    }
  };
  window['Game2048'] = Game2048;
})(window, document, jQuery);